package dk.jwood.android.graph;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.joda.time.DateTime;
import org.joda.time.Duration;

import android.util.Log;

public class GraphDef {
	
	private static final String TAG = "GraphDef";
	private int width = 0;
	private int height = 0;
	
	
	private int border = 20; 

	private float yMax = 1;
	private float yMin = 0;
	
	private Duration duration;
	private DateTime reference;

	private String xLabel;
	private String yLabel;
	

	private double pixelsPerMillis;
	
//	private double millisPerPixel;
	
	private double pixelsPerYPos;
	
	
	private List<Marker> horizontalMarkers;

	public GraphDef(Duration duration) {
		this.duration = duration;
		
	}

	public void update() {
		
		pixelsPerMillis = (double)(width - 2 * border) / duration.getMillis();
		

	}
	
	public void update(int width, int height, DateTime ref, Float minY, Float maxY) {
		
		//if(this.width== width && this.height == height) return;

		this.reference = ref;
		
		this.width = width;
		this.height = height;
		
		
		if(minY != null && maxY != null) {
			updateVerticalAxis(minY, maxY);
		}
		
		update();
		
	}

	private void updateVerticalAxis(Float minY, Float maxY) {
		
		// we need 20 pixels per line
		int verticalPoints = (height - border * 2) / 20;
		
		String format = "%1$.2f";
		float span = maxY - minY;
		if(span > 10) {
			format = "%1$.0f";
			if(minY != 0) {		
				int b2 = (int) (minY / 10);
				if(minY < 0) b2 -= 1;
				this.yMin = (float) Math.floor(b2 * 10);
			}
			if(maxY != 0) {		
				int b2 = (int) (maxY / 10);
				b2 += 1;
				this.yMax = (float) Math.ceil(b2 * 10);
			}
		} else if(span > 1) {
			format = "%1$.1f";
			yMin = (float) Math.floor(minY);
			yMax = (float) Math.ceil(maxY);
		} else {
			format = "%1$.2f";
			
			BigDecimal bdMin = new BigDecimal(minY);
			bdMin.round(new MathContext(2, RoundingMode.FLOOR));
			this.yMin = bdMin.floatValue();
			
			BigDecimal bdMax = new BigDecimal(maxY);
			bdMax.round(new MathContext(2, RoundingMode.CEILING));
			this.yMax = bdMax.floatValue();
		}
		
		float adjust = ((yMax - yMin) / verticalPoints);
		
		yMax += adjust;
		yMin -= adjust;
		
		float increments = (yMax - yMin) / verticalPoints;
		
		pixelsPerYPos = (height - 2 * border) / (double)(yMax - yMin);
	
		
		List<Marker> horizMarkerrs = new ArrayList<Marker>();
		float base = yMin;
		for(int i = 0; i < verticalPoints; i++) {
			Marker m = new Marker(translateValue(base), String.format(format, base));
			horizMarkerrs.add(m);
			//Log.d(TAG, "add marker "+m.getPosition()+" "+m.getText());
			base += increments;
			
		}
		
		horizontalMarkers = Collections.unmodifiableList(horizMarkerrs);
		
	}
	
	
	
	public String getxLabel() {
		return xLabel;
	}


	public void setxLabel(String xLabel) {
		this.xLabel = xLabel;
	}


	public String getyLabel() {
		return yLabel;
	}


	public void setyLabel(String yLabel) {
		this.yLabel = yLabel;
	}


	public double getPixelsPerMillis() {
		return pixelsPerMillis;
	}
	
	public Duration getDuration() {
		return duration;
	}


	public void setDuration(Duration duration) {
		this.duration = duration;
	}


	public DateTime getReference() {
		return reference;
	}


	public void setReference(DateTime reference) {
		this.reference = reference;
	}

	public int getBorder() {
		return border;
	}

	public void setBorder(int border) {
		this.border = border;
	}
	

	public float translateDuration(long duration) {
		return (float) (duration * pixelsPerMillis);
//		return (int) (millisPerPixel * reference.getMillis() - time);
		
	}
	
	public float translateTime(long time) {
		return (float) (width - ((reference.getMillis() - time)) * pixelsPerMillis) - border;
//		return (int) (millisPerPixel * reference.getMillis() - time);
		
	}
	
	public float translateValue(double value) {
		
		return (float) ((height - border) - (pixelsPerYPos * (value - this.yMin)));
		
//		return (int) (pixelsPerYPos * value - yMin);
		
	}
	
	
	public Iterator<DateTime> getXAxisSmallPoints(DateTime from) {
	
		return new SecondIterator(from, -1);
		
	} 
//	
//	public Iterator<DateTime> getXAxisLargePoints(DateTime from) {
//	
//		return new SecondIterator(from, -15);
//		
//	} 
	
	
	public static class SecondIterator implements Iterator<DateTime> {
		
		DateTime next;
		private final int increment;
		
		public SecondIterator(DateTime from, int increment) {
			this.increment = increment;
			if(increment > 0) {
				next = from.secondOfMinute().roundCeilingCopy();
				next.plusSeconds(increment - (next.getSecondOfMinute() % increment));
				
			} else {
				next = from.secondOfMinute().roundFloorCopy();
				next.minusSeconds(next.getSecondOfMinute() % increment);
				
			}
			
		}
		
		@Override
		public boolean hasNext() {
			return true;
		}

		@Override
		public DateTime next() {
			DateTime result = next;
			next = next.plusSeconds(increment);
			return result;
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
		
	}

	
	public class Marker {
		
		private final float position;  
		private final String text;
		public Marker(float position, String text) {
			super();
			this.position = position;
			this.text = text;
		}

		public float getPosition() {
			return position;
		}
		public String getText() {
			return text;
		}
		
		
	}
	

	public Iterator<Marker> getYAxisMarkers() {
		return horizontalMarkers == null ? Collections.<Marker>emptyList().iterator() : this.horizontalMarkers.iterator();
	}
	
	
	
}
